package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.common.cache.TsCache;
import com.atguigu.tingshu.common.cache.TsCacheAspect;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumAttributeValueVo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tencentcloudapi.ame.v20190916.models.Album;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Nullable;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private AlbumAttributeValueMapper albumAttributeValueMapper;

    @Autowired
    private AlbumStatMapper albumStatMapper;

    @Autowired
    private RabbitService rabbitService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private TsCacheAspect tsCacheAspect;


    @Override
    public IPage<AlbumListVo> findUserAlbumPage(Page<AlbumListVo> page, AlbumInfoQuery albumInfoQuery) {
//        1.已知前台传来的分页参数（页数、页码） 2、已知前台传来的查询参数
        /**
         * 需要通过两张表进行查询，这里把前端传的参传给mapper层执行
         */
        return albumInfoMapper.selectUserAlbumPage(page, albumInfoQuery);
    }


    /**
     * 保存专辑信息
     *
     * @param albumInfoVo
     * @param userId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveAlbumInfo(AlbumInfoVo albumInfoVo, Long userId) {
        try {
//        1.先新建专辑信息表的java-bean，往里面塞好属性后，把这个java-bean保存
            AlbumInfo albumInfo = new AlbumInfo();
//        2.属性赋值，先拷贝，再把不足的属性补上（前提：属性名必须一致）
            BeanUtils.copyProperties(albumInfoVo, albumInfo);//被拷贝属性放左，复印件放右
//        前端传递的数据是否都要保存到数据表，或保存数据表中的数据前端都传递了吗？（加校验）
//        需要知道当前专辑是谁创建的（获取当前登录用户id）userId: tracks_for_free（付费专辑的时候，先让用户进行试听）
//        seconds_for_free每集免费试听的秒数
            albumInfo.setUserId(userId);//设置该专辑所属的用户id

//        3.设定试听集数:先从前端取得声音的类型——是否付费。
            if (!SystemConstant.ALBUM_PAY_TYPE_FREE.equals(albumInfoVo.getPayType())) {
                //            是付费，就设置免费试听集数为5
                albumInfo.setTracksForFree(5);
            }
//        设置专辑为审核状态：发送消息给管理平台
            albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
//        表示执行insert方法，将专辑信息保存
            albumInfoMapper.insert(albumInfo);
//       搜出该专辑信息中的属性值
            List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
            if (!CollectionUtils.isEmpty(albumAttributeValueVoList)) { //如果该专辑属性值不为空
                //            ①循环遍历赋值
                for (AlbumAttributeValueVo albumAttributeValueVo : albumAttributeValueVoList) {
                    //                创建用来储存专辑属性值的容器
                    AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
                    //                给该容器赋各个属性值：
                    albumAttributeValue.setAlbumId(albumInfo.getId());
                    albumAttributeValue.setAttributeId(albumAttributeValueVo.getAttributeId());
                    albumAttributeValue.setValueId(albumAttributeValue.getValueId());
                    albumAttributeValueMapper.insert(albumAttributeValue);//把专辑属性值保存到数据库
                    //                以上操作的本质是，执行多次insert into 专辑属性值表 albumAttributeValue
                }
                //            后期可优化为批量保存
            }
//        4.保存专辑信息后，连续调用4次saveAlbumStat方法，分别传入了四种不同的统计类型：
//        用来统计该id专辑的播放量、订阅量、浏览量、评论数
            saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_PLAY); //播放量统计
            saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_SUBSCRIBE); //订阅量统计
            saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_BROWSE); //浏览量统计
            saveAlbumStat(albumInfo.getId(), SystemConstant.ALBUM_STAT_COMMENT); //评论数统计
//        判断当前专辑是否是公开；is_open=1;
            if ("1".equals(albumInfo.getIsOpen())){
//                如果是1，调用上架方法
                rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM,MqConst.ROUTING_ALBUM_UPPER,albumInfo.getId());
            }

        } catch (BeansException e) {
            log.error(e.getMessage(),e);
            return false;
        }
//        系统需要为每个专辑维护这四种核心数据建立统计记录
        return true;
    }

    public void saveAlbumStat(Long albumId, String statPlay) {
//        1.创建记录了专辑状态的容器
        AlbumStat albumStat = new AlbumStat();
//        2.设置好该状态容器对应的专辑id
        albumStat.setAlbumId(albumId);
//        3.设置写死的专辑播放量/订阅量/浏览量/评论数，直接赋个随机数
        albumStat.setStatNum(new Random().nextInt(10000000));
        albumStat.setStatType(statPlay);
//        4.在专辑状态库里插入该专辑id对应的状态容器信息
        albumStatMapper.insert(albumStat);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    @TsCache(keyPrefix = "album:",keySuffix = ":info")
    public AlbumInfo getAlbumInfo(Long albumId) {
        return getAlbumInfoDB(albumId);
////        1.定义redis中的string键名
//        String dataKey = RedisConstant.ALBUM_INFO_PREFIX+albumId;
//        try {
////        2.根据缓存的key获取数据
//            AlbumInfo albumInfo = (AlbumInfo) this.redisTemplate.opsForValue().get(dataKey);
////        3.判断缓存中是否存在数据
//            if (null == albumInfo){
//                System.out.println("缓存中没有数据！");
//    //            缓存中不存在，故查询数据库
//    //            redisson加锁，防止热key被多线程访问击穿
//                String lockKey = RedisConstant.ALBUM_LOCK_SUFFIX+albumId;
//                RLock lock = redissonClient.getLock(lockKey);
//                lock.lock();
//
//                try {
//    //            查询数据库之前，再判断一次缓存：
//                    albumInfo = (AlbumInfo) this.redisTemplate.opsForValue().get(dataKey);
//                    if (null != albumInfo){
//                        return albumInfo;
//                    }
//    //            执行业务逻辑——根据专辑id查询专辑信息：
//                    albumInfo = getAlbumInfoDB(albumId);
//    //                判断数据库中该数据是否为空：
//                    if (null == albumInfo){
//                        System.out.println("缓存中没有数据！");
//    //              如果为空，为了防止穿透，设置1个空值,但存在过期时间，10分钟后空值消失
//                        this.redisTemplate.opsForValue().set(dataKey,new AlbumInfo(),RedisConstant.ALBUM_TEMPORARY_TIMEOUT, TimeUnit.SECONDS);
//                        return new AlbumInfo();
//                    }
//    //                数据库中有数据,就存入缓存中：
//                    this.redisTemplate.opsForValue().set(dataKey,albumInfo,RedisConstant.ALBUM_TIMEOUT,TimeUnit.SECONDS);
//    //                返回数据：
//                    return albumInfo;
//                } catch (Exception e) {
//                    log.error(e.getMessage(),e);
//                    throw new RuntimeException(e);
//                } finally {
//                    // 解锁：
//                    lock.unlock();
//                }
//            } else {
//    //            缓存有数据的情况：
//                System.out.println("缓存中存在数据！");
//                return albumInfo;
//            }
//        } catch (RuntimeException e) {
//           log.error(e.getMessage(),e);
//        }
////        数据库兜底操作
//        return getAlbumInfoDB(albumId);
    }

    @Nullable
    private AlbumInfo getAlbumInfoDB(Long albumId){
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        if (null != albumInfo){
            LambdaQueryWrapper<AlbumAttributeValue> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(AlbumAttributeValue::getAlbumId,albumId);
            List<AlbumAttributeValue> albumAttributeValueList = albumAttributeValueMapper.selectList(wrapper);
            albumInfo.setAlbumAttributeValueVoList(albumAttributeValueList);
            return albumInfo;
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateAlbumInfo(Long albumId, AlbumInfoVo albumInfoVo) {
//        update album_info set title = ? where id = ?
        AlbumInfo albumInfo = new AlbumInfo();
//        进行专辑更新用到的数据不是前端传的Vo数据,需要进行数据类型转换
//        ①属性值拷贝：
        BeanUtils.copyProperties(albumInfoVo,albumInfo);
//        ②把前端传的专辑id设置进专辑信息数据中去
        albumInfo.setId(albumId);
        albumInfoMapper.updateById(albumInfo);
//        ③由于更新专辑属性值信息时，没有触发其他controller，所以不能实时记录修改信息，只能先删除再添加
//          - 删除原有标签数据：
        albumAttributeValueMapper.delete(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId,albumId));
        List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        if (!CollectionUtils.isEmpty(albumAttributeValueVoList)){
//            循环遍历修改数据
            for (AlbumAttributeValueVo albumAttributeValueVo : albumAttributeValueVoList) {
//                ①创建更新的实体对象
                AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
//                ②属性拷贝
                BeanUtils.copyProperties(albumAttributeValueVo,albumAttributeValue);
                albumAttributeValue.setAlbumId(albumId);
                albumAttributeValueMapper.insert(albumAttributeValue);

            }
        }
        //        判断当前专辑是否是公开；is_open=1;
        if ("1".equals(albumInfo.getIsOpen())){
//                如果是1，调用上架方法
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM,MqConst.ROUTING_ALBUM_UPPER,albumInfo.getId());
        }else {
//           调用下架方法：
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM,MqConst.ROUTING_ALBUM_LOWER ,albumInfo.getId());

        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeAlbumInfo(Long albumId) {
//        删除album_info
        albumInfoMapper.deleteById(albumId);
//        删除专辑属性值表中的相应信息
        albumAttributeValueMapper.delete(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId,albumId));
//        删除统计信息表中的相应数据
        albumStatMapper.delete(new LambdaQueryWrapper<AlbumStat>().eq(AlbumStat::getAlbumId,albumId));
    }

    @Override
    public List<AlbumInfo> findUserAllAlbumList(Long userId) {
//        1.构建查询条件
        LambdaQueryWrapper<AlbumInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AlbumInfo::getUserId,userId);
        wrapper.orderByDesc(AlbumInfo::getId);
        Page<AlbumInfo> albumInfoPage = new Page<>(1,100);
        Page<AlbumInfo> infoPage = albumInfoMapper.selectPage(albumInfoPage,wrapper);
//        2.返回专辑列表
        return infoPage.getRecords();
    }

    @Override
    @TsCache(keyPrefix = "statVo:")
    public AlbumStatVo getAlbumStatVoByAlbumId(Long albumId) {
        return albumInfoMapper.selectAlbumStat(albumId);
    }

    @Override
    public void updateStat(Long albumId, String statType, Integer count) {
        albumInfoMapper.updateStat(albumId,statType,count);
    }

}
